﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using xplang.abstractions;
using xplang.run;

namespace xplang.rtl.native
{
  public class DoubleClass : NativeClass
  {

    public ClassMethod mRound;

    public DoubleClass()
      : base(RTL.rtl, "Double")
    {
      genericParams = new GenericParamDecl[] { };
      ClassRef _double = getRef(null);
      ClassRef _int = xplang.rtl.RTL.intClass.getRef(null);
      inheritance = new ClassRef[]{
        xplang.rtl.RTL.equatableClass.getRef(new ClassRef[]{_double}),
        xplang.rtl.RTL.equatableClass.getRef(new ClassRef[]{_int}),
        xplang.rtl.RTL.equatableClass.getRef(new ClassRef[]{_double}),
        xplang.rtl.RTL.comparableClass.getRef(new ClassRef[]{_double}),
        xplang.rtl.RTL.comparableClass.getRef(new ClassRef[]{_int}),
        xplang.rtl.RTL.addableClass.getRef(new ClassRef[]{_double}),
        xplang.rtl.RTL.addableClass.getRef(new ClassRef[]{_int}),
        xplang.rtl.RTL.incrementableClass.getRef(null),
        xplang.rtl.RTL.decrementableClass.getRef(null)
      };
    }

    public override void registerMembers()
    {
      xplang.rtl.RTL.comparableClass.mGE.registerImplementation(this, execGE);
      xplang.rtl.RTL.comparableClass.mGT.registerImplementation(this, execGT);
      xplang.rtl.RTL.comparableClass.mLE.registerImplementation(this, execLE);
      xplang.rtl.RTL.comparableClass.mLT.registerImplementation(this, execLT);

      xplang.rtl.RTL.addableClass.mAdd.registerImplementation(this, execAdd);
      xplang.rtl.RTL.incrementableClass.mInc.registerImplementation(this, execInc);
      xplang.rtl.RTL.decrementableClass.mDec.registerImplementation(this, execDec);

      mRound = new NativeClassMethod(this, Modifier.None,
        xplang.rtl.RTL.intClass.getRef(null),
        "round",
        new Variable[] { });
      methods.Add(mRound);
      mRound.registerImplementation(this, execRound);
    }

    public override object newInstance()
    {
      return new Double();
    }

    public override Value getAttributeValue(Value _this, Variable variable)
    {
      throw new NotImplementedException();
    }

    public override void setAttributeValue(Value _this, Variable variable, Value obj)
    {
      throw new NotImplementedException();
    }

    public Value execGE(Value[] heap, ref FlowState state)
    {
      Double l = (Double)heap[0].pointer;
      Double r = heap[1].pointer is Int64 ? (Int64)(heap[1].pointer) : (Double)(heap[1].pointer);
      return new Value(xplang.rtl.RTL.booleanClass.getRef(null), l >= r);
    }

    public Value execGT(Value[] heap, ref FlowState state)
    {
      Double l = (Double)heap[0].pointer;
      Double r = heap[1].pointer is Int64 ? (Int64)(heap[1].pointer) : (Double)(heap[1].pointer);
      return new Value(xplang.rtl.RTL.booleanClass.getRef(null), l > r);
    }

    public Value execLE(Value[] heap, ref FlowState state)
    {
      Double l = (Double)heap[0].pointer;
      Double r = heap[1].pointer is Int64 ? (Int64)(heap[1].pointer) : (Double)(heap[1].pointer);
      return new Value(xplang.rtl.RTL.booleanClass.getRef(null), l <= r);
    }

    public Value execLT(Value[] heap, ref FlowState state)
    {
      Double l = (Double)heap[0].pointer;
      Double r = heap[1].pointer is Int64 ? (Int64)(heap[1].pointer) : (Double)(heap[1].pointer);
      return new Value(xplang.rtl.RTL.booleanClass.getRef(null), l < r);
    }

    public Value execAdd(Value[] heap, ref FlowState state)
    {
      Double l = (Double)heap[0].pointer;
      Double r = heap[1].pointer is Int64 ? (Int64)(heap[1].pointer) : (Double)(heap[1].pointer);
      return new Value(xplang.rtl.RTL.intClass.getRef(null), l + r);
    }

    public Value execInc(Value[] heap, ref FlowState state)
    {
      return new Value(xplang.rtl.RTL.doubleClass.getRef(null), ((Double)heap[0].pointer) + 1);
    }

    public Value execDec(Value[] heap, ref FlowState state)
    {
      return new Value(xplang.rtl.RTL.doubleClass.getRef(null), ((Double)heap[0].pointer) - 1);
    }

    public Value execRound(Value[] heap, ref FlowState state)
    {
      Double d = ((Double)heap[0].pointer);
      return new Value(xplang.rtl.RTL.doubleClass.getRef(null), Convert.ToInt64(Math.Round(d)));
    }
  }
}
